import React, { createContext, useContext, useEffect, useState, useMemo } from 'react'
import { bindActionCreators } from 'redux'
// 创建一个上下文对象
const ThemeContext = createContext()

// Provider:把传递进来的store放在根组件的上下文中
export function Provider(props) {
    let { children, store } = props
    return <ThemeContext.Provider value={{
        store
    }}>
        {children}
    </ThemeContext.Provider>
}

/**
connect:获取上下文中的store，然后把公共状态、要派发的方法等，都基于属性传递给需要渲染的组件;
把让组件更新的方法放在事件更新池中
 *  */

export function connect(mapStateToProps, mapDispatchToProps) {

    // 处理默认值
    if (!mapStateToProps) {
        mapStateToProps = () => {
            // 调用时如果不写这个参数则什么都不传
            return {}
        }
    }

    if (!mapDispatchToProps) {
        mapDispatchToProps = (dispatch) => {
            // 不写则把dispatch方法传递给组件
            return {
                dispatch
            }
        }
    }

    // 返回一个函数，在使用的时候传递一个组件进来
    return function currying(Component) {
        // Component:最终要渲染的组件 【Vote】
        // HOC:我们最后基于export default 导出的组件，将传进来的组件处理，组件通过props获取状态值
        return function HOC(props) {  // 外面在调用组件，并且传递参数的时候，其实是将参数传递给了HOC
            // 我们需要获取上下文中的store
            let { store } = useContext(ThemeContext),
                { getState, dispatch, subscribe } = store
            // 向事件池中加入让组件更新的方法
            let [, forceUpdate] = useState(0)

            useEffect(() => {
                let unsubscribe = subscribe(() => {
                    forceUpdate(+new Date())
                })
                return () => {
                    unsubscribe()
                }
            }, [])

            // 把mapStateToProps/mapDispatchToProps,把执行的返回值，作为属性传递给组件！！
            let state = getState(),  // 获取所有的公共状态
                nextState = useMemo(() => {
                    return mapStateToProps(state)
                }, [state])

            let dispatchProps = {}

            if (typeof mapDispatchToProps === "function") {
                // 是函数就直接执行
                dispatchProps = mapDispatchToProps(dispatch)
            } else {
                //  是actionCreators对象，需要经过bindActionCreators处理
                dispatchProps = bindActionCreators(mapDispatchToProps, dispatch) // 这是bindActionCreator 这个方法需要传递的两个参数
            }

            return <Component {...props} {...nextState} {...dispatchProps} />
        }
    }
}
